package com.dzj.exam.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dzj.exam.entity.Banner;
import com.dzj.exam.entity.Category;
import com.dzj.exam.entity.Question;
import com.dzj.exam.mapper.BannerMapper;
import com.dzj.exam.mapper.CategoryMapper;
import com.dzj.exam.mapper.QuestionMapper;
import com.dzj.exam.service.CategoryService;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Slf4j
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private QuestionMapper questionMapper;


    @Override
    public List<Category> getAllCategories() {

        // 1. 获取所有分类的基础信息，并按sort字段排序
        List<Category> categories = categoryMapper.selectList(
                new LambdaQueryWrapper<Category>()
                        .orderByAsc(Category::getSort)
        );

        // 2. 为分类列表填充题目数量【进行子分类和count数量填充】
        fillQuestionCount(categories);

        return categories;
    }

    @Override
    public List<Category> getCategoryTree() {

        // 1. 获取所有分类，并按sort排序
        List<Category> allCategories = categoryMapper.selectList(
                new LambdaQueryWrapper<Category>()
                        .orderByAsc(Category::getSort)
        );

        // 2. 为每个分类填充其自身的题目数量
        fillQuestionCount(allCategories);

        // 3. 构建树形结构并返回
        List<Category> buildTree = buildTree(allCategories);
        log.info("查询类别树状结构集合：{}",buildTree);
        return buildTree;
    }

    /**
     * 保存分类信息
     *   需要检查名称是否重复！
     * @param category
     */
    @Override
    public void addCategory(Category category) {
        //1.判断同一个父类分类下不允许重名
        // parent_id = 传入 and name = 传入
        LambdaQueryWrapper<Category> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Category::getParentId, category.getParentId());
        lambdaQueryWrapper.eq(Category::getName,category.getName());
        long count = count(lambdaQueryWrapper);// count 查询存在的数量
        //知识点： 我们可以在自己的service获取自己的mapper -> CategoryMapper baseMapper = getBaseMapper();
        if (count > 0) {
            Category parent = getById(category.getParentId());
            //不能添加，同一个父类下名称重复了
            throw new RuntimeException("在%s父分类下，已经存在名为：%s的子分类，本次添加失败！".formatted(parent.getName(),category.getName()));
        }
        //2.保存
        save(category);
    }

    @Override
    public void updateCategory(Category category) {
        boolean exists = checkAndUpdate(category);
        if (exists) {
            Category parent = getById(category.getParentId());
            //不能添加，同一个父类下名称重复了
            throw new RuntimeException("在%s父分类下，已经存在名为：%s的子分类，本次更新失败！".formatted(parent.getName(),category.getName()));
        }
        //2.再更新
        updateById(category);
    }

    @Override
    public void deleteCategory(Long id) {
        //1.检查是否一级标题
        Category category = getById(id);
        if (category.getParentId() == 0){
            throw new RuntimeException("不能删除一级标题！");
        }
        //2.检查是否存在关联的题目
        LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Question::getCategoryId,id);
        long count = questionMapper.selectCount(lambdaQueryWrapper);
        if (count>0){
            throw new RuntimeException("当前的:%s分类，关联了%s道题目,无法删除！".formatted(category.getName(),count));
        }
        //3.以上不都不满足，删除即可【子关联数据，一并删除】
        removeById(id);
    }

    //校验和父类下是否有同名
    private Boolean checkAndUpdate(Category category) {
        LambdaQueryWrapper<Category> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Category::getParentId, category.getParentId()); // 同一父分类下！
        lambdaQueryWrapper.ne(Category::getId, category.getId());
        lambdaQueryWrapper.eq(Category::getName, category.getName());
        CategoryMapper categoryMapper = getBaseMapper();
        return categoryMapper.exists(lambdaQueryWrapper);
    }

    // 构建树形结构的私有辅助方法
    private List<Category> buildTree(List<Category> categories) {
        // 1. 使用Stream API按parentId进行分组，得到 Map<parentId, List<children>>
        Map<Long, List<Category>> childrenMap = categories.stream()
                .collect(Collectors.groupingBy(Category::getParentId));

        /*
            stream()：把 List<Category> 转成 Stream 流，开启流式操作。
            Collectors.groupingBy：按指定规则分组，这里用方法引用 Category::getParentId ，提取分类的 parentId 作为分组 key，value 是对应 parentId 的分类列表，快速构建 父 ID - 子分类列表 映射。
         */

        // 2. 遍历所有分类，为它们设置children属性，并递归地累加题目数量
        categories.forEach(category -> {
            // 从Map中找到当前分类的所有子分类
            List<Category> children = childrenMap.getOrDefault(category.getId(), new ArrayList<>());
            category.setChildren(children);

            // 汇总子分类的题目数量到父分类
            long childrenQuestionCount = children.stream()
                    .mapToLong(c -> c.getCount() != null ? c.getCount() : 0L)
                    .sum();
            /*
                forEach：遍历每个分类，对单个分类做处理，类似增强 for 循环，但结合 Stream 更灵活。
                getOrDefault：从分组好的 childrenMap 取当前分类的子分类，无对应值时给默认空列表，避免空指针。
                嵌套 stream().mapToLong().sum()：先转成 LongStream ，通过 mapToLong 处理 count （空值转 0 ），再用 sum 汇总子分类题目数，结合自身题目数，设置到当前分类，完成递归汇总逻辑。
             */

            long selfQuestionCount = category.getCount() != null ? category.getCount() : 0L;
            // 父分类的总数 = 自身的题目数 + 所有子分类的题目数总和
            category.setCount(selfQuestionCount + childrenQuestionCount);
        });

        // 3. 最后，筛选出所有顶级分类（parentId为0），它们是树的根节点
        /*
            filter：按条件（parentId == 0 ）过滤分类，只保留顶级分类。
            collect(Collectors.toList())：把过滤后的 Stream 流转为 List ，作为分类树的根节点集合返回。
         */
        return categories.stream()
                .filter(c -> c.getParentId() == 0)
                .collect(Collectors.toList());
    }

    // 这是一个私有的辅助方法，用于填充题目数
    private void fillQuestionCount(List<Category> categories) {
        // 1. 一次性查询出所有分类的题目数量
        List<Map<Long, Object>> questionCountList = questionMapper.getCategoryQuestionCount();

        // 2. 将查询结果从 List<Map> 转换为 Map<categoryId, count>，便于快速查找
        Map<Long, Long> questionCountMap = questionCountList.stream().collect(Collectors.toMap(
                map -> Long.valueOf(map.get("category_id").toString()),
                map -> Long.valueOf(map.get("count").toString())
        ));

        // 3. 遍历分类列表，为每个分类设置其对应的题目数量
        categories.forEach(category -> {
            category.setCount(questionCountMap.getOrDefault(category.getId(),0L));
        });
    }
}